/*
 * compressedStream.cpp
 *
 *  Created on: May 25, 2012
 *      Author: Nico
 */

#ifndef COMPRESSEDSTREAM_H_
#define COMPRESSEDSTREAM_H_

#include "BitFileManager.cpp"
#include "compressedStream.h"


OCompressedStream::OCompressedStream(const char* outputFilename,uInt blockSize){
	//this->bitManager = new OBitFileManager(outputFilename);
	this->output = new ofstream( outputFilename , ios::binary );
	
	this->blockSize = blockSize;
	this->includeBlockSize = true;
	this->blockSizeIncluded = false;
}

OCompressedStream::~OCompressedStream(){
	//delete this->bitManager;
	delete this->output;
}


int OCompressedStream::writeIntAsGamma(uInt number){
	string gamma = intToGamma(number);
	return this->bitManager->writeBinaryString(&gamma);
}

int OCompressedStream::writeNumber(uInt number){
	char num[6];
	sprintf(num , "%6d" , number);
	*this->output << num;
	return 0;
}

int OCompressedStream::writeModeloEstructuradoResult (const vector<string>* resultadosDeCadaNivel) {
	
	if (resultadosDeCadaNivel -> size() > 0 ) {
		
		// Guardo al principio de todo el tamanio del resultado de cada nivel
		unsigned int j;
		for (j=0; j < resultadosDeCadaNivel->size(); j++) {
			cout << "Guardando ...." << resultadosDeCadaNivel->at(j).length() << endl;
			writeNumber((uInt)resultadosDeCadaNivel->at(j).length());
		}
		
		// Ahora guardo el resultado de cada nivel
		unsigned int i;
		for (i=0; i < resultadosDeCadaNivel->size(); i++) {
			*this->output << resultadosDeCadaNivel->at(i);
			cout << "Resultados guardados." << endl;
			}
	}
	return 0;
}

int OCompressedStream::writeMTFResult(vector<uChar>* vectorR) {

	if (vectorR -> size() > 0) {
		uInt i;
		for (i = 0; i < vectorR->size(); i++) {
			*this->output << (vectorR -> at (i));
			//cout << "Byte guardado." << endl;
		}
	}
	
	return 0;
}

int OCompressedStream::writeText( string* text ){
	*this->output << *text;
	return 0;
}

int OCompressedStream::writeRLEresult( string* text ){
	return this->writeRLEresult( text );
}

int OCompressedStream::writeBlockSortingResult( string* L , uInt I ){
	if( this->includeBlockSize and !this->blockSizeIncluded ){
		this->blockSizeIncluded = true;
		//this->writeIntAsGamma( this->blockSize );
		this->writeNumber( this->blockSize );
	}
	
	// Primero escribo I en gamma
	//this->writeIntAsGamma( I );
	this->writeNumber(I);
	
	// Luego escribo el texto
	//this->bitManager->writeText(L);
	
	this->writeText(L);
	
	return 0;
}


ICompressedStream::ICompressedStream(const char* inputFilename){
	this->inputFilename = (char*) inputFilename;
	//this->bitManager = new IBitFileManager(this->inputFilename);
	this->input = new ifstream( inputFilename );
	
	this->lastReadAmount = 0;
	
	this->blockSize = 0;
	this->startWithBlockSize = true;
	this->blockSizeReaded = false;
	
	this->debug = false;
}

ICompressedStream::ICompressedStream(const char* inputFilename , uInt blockSize){
	//if( blockSize == 0 ) return ERROR_BLOCKSIZE;
	this->inputFilename = (char*) inputFilename;
	//this->bitManager = new IBitFileManager(inputFilename);
	this->input = new ifstream( inputFilename );
	
	this->lastReadAmount = 0;
	
	this->blockSize = blockSize;
	this->startWithBlockSize = false;
	this->blockSizeReaded = false;
	
	this->debug = false;
}


ICompressedStream::~ICompressedStream(){
	
	//delete this->bitManager;
	this->input->close();
	delete this->input;
}

int ICompressedStream::readGamma(uInt* num){
	string aux;
	return this->readGamma(num,&aux);
}

int ICompressedStream::readGamma(uInt* num , string* gamma ){
	uChar bit;
	short contCeros = 0;
	
	gamma->clear();
	
	while(true){
		if( ERROR_EOF == this->bitManager->getNextBit( &bit ) ) return ERROR_EOF;
		
		gamma->push_back(bit);
		if( bit == '0' ){
			contCeros++;
		}else if( bit == '1' ){
			break;
		}
	}
	
	string numeroBinario = "";
	if( contCeros > 0 ){
		if( ERROR_EOF == this->bitManager->getNextBits(contCeros, &numeroBinario ) ) return ERROR_EOF;
		
		string::iterator it;
		for ( it=numeroBinario.begin() ; it < numeroBinario.end(); it++ ){
			gamma->push_back(*it);
		}
		
		numeroBinario = "1" + numeroBinario;
	}else{
		*num = 1;
		return 0;
	}

	*num = (uInt)stringBinToInt( (char*)numeroBinario.c_str() );
	
	return 0;
}

int ICompressedStream::readNumber(uInt* number){
	
	char byte; string num;
	uInt i=0;
	int status = 0;
	
	while(this->input->good()){
		if(i==6) break;
		byte = this->input->get();
		if( !this->input->good() ){
			num.push_back(byte);
			status = ERROR_EOF;
			break;
		}else{
			num.push_back(byte);
		}
		i++;
	}

	//cout << "Leido: '" << num << "'" <<  endl;
	
	*number = atoi(num.c_str());
	
	return status;
}

int ICompressedStream::readByte(uChar* byte){
	if( ERROR_EOF == this->bitManager->getNextByte(byte) ) return ERROR_EOF;
	return 0;
}

// Lee un bloque entero (longitud en bytes)
int ICompressedStream::readBlock(uInt blockSize,char** bytes){
	
	this->input->read(*bytes,blockSize);
	this->lastReadAmount = this->input->gcount(); 
	
	//*bytes[blockSize] = '\0'; cout << "Reemplazado!" << endl;
	
	if( this->input->eof() ){
		return ERROR_EOF;
	}
		
	return 0;
}

// Lee un bloque entero (longitud en bytes)
int ICompressedStream::readBlock(uInt block_size,string* bytes){
	//return this->bitManager->getNextBytes(blockSize, bytes);
	
	char* buffer = new char[blockSize];
	memset( buffer , '\0' , block_size );
	int status = this->readBlock(block_size,&buffer);
		
	bytes->clear();
	bytes->assign(buffer,blockSize);
		
	delete[] buffer;
	
	return status;
}

// Utiliza this->blockSize como tamanio del bloque
int ICompressedStream::readBlock(char** bytes){
	return this->readBlock( this->blockSize , bytes );
}

// Utiliza this->blockSize como tamanio del bloque
int ICompressedStream::readBlock(string* bytes){
	return this->readBlock( this->blockSize , bytes );
}

int ICompressedStream::readMTFresult (char** result) {
	if( ERROR_EOF == this->readBlock(result) ){
		if(this->debug) cout << "EOF Leyendo bloque" << endl;
		return ERROR_EOF;
	}
	
	return 0;
}

int ICompressedStream::readBlockSortingResult( string* L , uInt* I ){
	
	if( (this->startWithBlockSize == true) and (this->blockSizeReaded == false) ){
		this->blockSize = 0;
		//if( ERROR_EOF == this->readGamma(&this->blockSize) ) return ERROR_EOF;
		if( ERROR_EOF == this->readNumber(&this->blockSize) ) return ERROR_EOF;
		if( this->blockSize == 0 ) return ERROR_BLOCKSIZE;
		this->blockSizeReaded = true;
		
		if(this->debug) cout << "Blocksize: " << this->blockSize << endl;
	}
	
	
	
	/*
	string gamma;
	if( ERROR_EOF == this->readGamma(I,&gamma) ) {
		if(this->debug) cout << "EOF Leyendo gamma" << endl;
		return ERROR_EOF;
	}
	*/ 
	if( ERROR_EOF == this->readNumber(I) ) {
		return ERROR_EOF;
	}
	
	//if(this->debug) cout << "Gamma: " << *I << " (" << gamma << ")" << endl;
	if(this->debug) cout << "I: " << *I << endl;
	
	
	if( ERROR_EOF == this->readBlock(L) ){
		if(this->debug) cout << "EOF Leyendo bloque" << endl;
		return ERROR_EOF;
	}
	
	return 0;
}

#endif /* COMPRESSEDSTREAM_H_ */
